Live Photos from Ordinary Videos PRO
Turn an ordinary video you already have — a screen recording, a clip from the Photos app, something you downloaded — into a real Live Photo in the system photo library.
That's the whole flow. Everything below is detail you only need when you want to control the result.
Why you can't just pair any photo with any video
A Live Photo is not "a JPEG next to an MP4". The photo library only accepts the two files as a pair when three things line up:
- The still image carries a Live Photo asset identifier in its Apple Maker Note.
- The movie carries the same identifier as its content identifier.
- The movie has a still-image-time metadata track marking which moment is the cover frame.
Grab a frame with AVAsset, re-encode it with UIImage.toJPEGData(), hand both files to Photos.saveLivePhoto — and it fails with PHPhotosErrorDomain 3302, because a decoded bitmap has no identifier and a plain .mp4 has no metadata track.
LivePhoto.createFromVideo writes all three for you. You never touch the metadata yourself.
Choosing the cover frame
The still image is the frame your video is "paused on" in the library. By default it's the middle of the source. Pass stillTime (in seconds from the start of the source video) to pick another moment:
The kept portion of the video is centred on that moment. A value past the end of the video is clamped rather than rejected, so you can pass a rough guess safely.
Length
Live Photos are usually short. maxDuration defaults to 10 seconds — a longer source is trimmed to a window around stillTime:
Note that stillTime on the way in is relative to the source video, while stillTime on the way out is relative to the trimmed output.
There is no upper limit beyond the source video itself. Ask for a longer clip, or for the whole thing:
Length costs you almost nothing while converting — the video track is copied rather than re-encoded, so a 60-second clip is mostly file I/O. Two things to keep in mind, though: a long Live Photo is still a single item in the photo library and takes up the space of the whole video, and how the library plays back a very long one is up to iOS. The only case where length really costs time is the rare re-encode fallback — check reencoded in the result if that matters to you.
Output files
Both files land in the temporary directory unless you say otherwise, and the returned paths are always the ones actually written:
The paired movie must be a .mov; the photo library does not accept .mp4 as a paired video, so a non-.mov output path is rejected up front. The still image can be "jpeg" (default) or "heic".
If you don't want the original sound carried into the Live Photo, pass includeAudio: false.
What you get back
reencoded is false for almost every video: the original video track is copied across untouched, which is fast and lossless. It flips to true when the source encoding can't live in a QuickTime container and the video had to be re-encoded — slower, and slightly lossy.
Orientation
Rotation is handled for you. A portrait video keeps its orientation in the movie, and the still image is written already rotated to match, so the cover frame and the playback never disagree.
Errors
The promise rejects, with a readable message, when:
- the source file doesn't exist, or isn't a media file;
- the source has no video track (an audio-only file, for example);
- the video output path doesn't end in
.mov, or its folder doesn't exist; - the video has no playable duration.
Nothing partial is left behind — if the movie can't be written, the still image is cleaned up too.
Saving to the library
Photos.saveLivePhoto copies both files into the library. Pass shouldMoveFile: true only if you're done with them, since it moves the files away rather than copying:
Inspecting the identifier
If you're curious (or debugging a pairing problem), the identifier is readable through ImageIO:
You can also supply your own identifier — for instance to rebuild a pair with the same identity — but it must be a UUID string. The field that stores it inside the still image is fixed-length, so anything shorter is padded and would no longer match the movie; a non-UUID value is rejected rather than silently producing a pair the library won't accept.
Related
- Live Photo capture — if the camera is the source,
AVCapturePhotoOutputcan record a Live Photo directly; see the Live Photo page under AVCaptureSession. You don't needcreateFromVideoin that case. - LivePhotoView — display the result in your UI.
- ImageIO — read and write image metadata, including
makerApple.
